<?php

class GravatarClient
{
  protected 
    $serviceUri = 'http://www.gravatar.com/avatar',
    $cacheLifetime = 86400,
    $cacheDir = null,
    $email = null,
    $parameters = array()
  ;

  static protected $defaults = array('identicon', 'wavatar', 'monsterid');
  static protected $ratings  = array('g', 'pg', 'r', 'x');

  public function __construct($email, $size = 80, $rating = 'g')
  {
    $this->setEmail($email);
    $this->setSize($size);
    $this->setRating($rating);
  }

  public function setCacheDir($dir)
  {
    if (!file_exists($dir))
    {
      throw new Exception(sprintf('Directory "%s" does not exist on the filesystem.', $dir));
    }
    
    if (!is_writable($dir))
    {
      throw new Exception(sprintf('Directory "%s" is not writeable.', $dir));
    }
    
    $this->cacheDir = $dir;
  }
  
  public function getCacheDir()
  {
    return $this->cacheDir;
  }

  public function setCacheLifetime($lifetime)
  {
    $this->cacheLifetime = (int) $lifetime;
  }
  
  public function getCacheLifetime()
  {
    return $this->cacheLifetime;
  }

  public function setDefault($default)
  {
    // Is default an URI ?
    $bool = filter_var($default, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED);
    
    if (!$bool)
    {
      // Is default a gravatar value ?
      $default = strtolower($default);
      if (!in_array($default, self::$default))
      {
        throw new Exception(sprintf('Default picture %s is not valid.', $default));
      }
    }
    
    $this->parameters['d'] = $default;
  }
  
  public function getDefault()
  {
    return array_key_exists('d', $parameters) ? $this->parameters['d'] : '';
  }

  public function setSize($size)
  {
    $size = (int) $size;

    if ($size < 1 || $size > 512)
    {
      throw new Exception(sprintf('Size "%u" is not a valid size. It must be between 1 and 512 pixels', $size));
    }

    $this->parameters['s'] = $size;
  }

  public function getSize()
  {
    return (int) $this->parameters['s'];
  }

  public function setEmail($email)
  {
    if (!filter_var($email, FILTER_VALIDATE_EMAIL))
    {
      throw new Exception(sprintf('Email "%s" is not a valid email address.', $email));
    }
  
    $this->email = $email;
  }

  public function getEmail()
  {
    return $this->email;
  }

  public function getHashedEmail()
  {
    return md5(strtolower($this->email));
  }

  public function setRating($rating)
  {
    $rating = strtolower($rating);

    if (!in_array($rating, self::$ratings))
    {
      throw new Exception(sprintf('Rating %s is not a valid rating.', $rating));
    }

    $this->rating = $rating;
  }

  public function getRating()
  {
    return $this->rating;
  }

  public function getFilePath()
  {
    return $this->getCacheDir().'/'.$this->getCachedFilename();
  }

  public function isExpired()
  {
    return filemtime($this->getFilePath()) + $this->getCacheLifetime() < time();
  }

  public function getCachedFilename()
  {
    return sprintf(
      '%s_%s%u.jpg', 
      $this->getHashedEmail(), 
      $this->getRating(), 
      $this->getSize()
    );
  }

  public function get()
  {
    if (!$this->getCacheDir())
    {
      return $this->getServiceUri();
    }

    if (!file_exists($this->getFilePath()) || $this->isExpired())
    {
      if (false === file_put_contents($this->getFilePath(), file_get_contents($this->getServiceUri())))
      {
        throw new Exception('Unable to retrieve the corresponding gravatar.');
      }
    }

    return $this->getCachedFilename();
  }

  public function getServiceUri()
  {
    return sprintf(
      '%s/%s?%s',
      $this->serviceUri,
      $this->getHashedEmail(),
      http_build_query($this->parameters)
    );
  }
}